package gim_broker

import (
	"fmt"
	"gim-broker/utils"
	"google.golang.org/protobuf/proto"
	"log"
	"net"
	"strings"
)

type TcpServer struct {
	Port int32
}

func (s *TcpServer) Bind() (err error) {

	address := fmt.Sprintf("%s%s%d", "0.0.0.0", ":", s.Port)
	addr, _ := net.ResolveTCPAddr("tcp", address)
	listener, err := net.ListenTCP("tcp", addr)
	if err != nil {
		fmt.Println(err)
		return err
	}
	Logger.Error("gim-broker启动成功")
	go removeInvalidClient()
	for {
		conn, err := listener.AcceptTCP()
		if err != nil {
			fmt.Println(err)
			continue
		}
		c := NewHeartBeatInfo(conn)    // 客户端对应的心跳数据结构
		go HandlerConn(conn, c)        // 处理客户端数据交互
		go heartBeatDetection(conn, c) // 处理客户端心跳
	}

}

func getIp(conn *net.TCPConn) string {
	return strings.Split(conn.RemoteAddr().String(), ":")[0]
}

func HandlerConn(conn *net.TCPConn, c *heartBeatInfo) {

	Logger.Infof("客户端[连接] ip %s", getIp(conn))
	for {
		command, data, err := decode(conn)
		if err != nil {
			log.Println("Failed to read message:", err)
			// 释放资源
			releaseByConn(conn)
			return
		}
		Logger.Info("###################")
		Logger.Info(command)
		Logger.Info(utils.HexDump(data))
		Logger.Info("******************")
		c.heartbeat <- data // 更新收到的最后一条信息用于判断多久没有收到客户端数据
		switch command {
		case CommandHeartbeat:
			//heartbeat(conn, data)
			// todo 心跳信息中暂时没有加入额外信息，如版本，身份信息等，可以展示不处理
			break
		case CommandJoin:
			join(conn, data)
			break
		case CommandQuit:
			quit(data)
			break
		case CommandTransfer:
			err := Transfer(data)
			if err != nil {
				Logger.Error(err)
			}
			break
		case CommandJoinGroup:
			joinGroup(data)
		}
	}

}

/*
*
心跳数据
*/
func heartbeat(conn *net.TCPConn, data []byte) {

	msg := &HeartbeatMsg{}
	err := proto.Unmarshal(data, msg)
	if err != nil {
		Logger.Error(err)
		return
	}
}

/*
	客户端加入

conn 连接信息
data 消息数据
*/
func join(conn *net.TCPConn, data []byte) {

	msg := &JoinMsg{}
	err := proto.Unmarshal(data, msg)
	if err != nil {
		Logger.Error(err)
		return
	}
	ip := getIp(conn)
	client := &Client{ClientId: msg.ClientId, Ip: ip, Conn: conn}
	_ = PutClient(client)
}

/*
*
客户端退出，释放相关资源
*/
func quit(data []byte) {

	msg := &QuitMsg{}
	err := proto.Unmarshal(data, msg)
	if err != nil {
		Logger.Error(err)
		return
	}
	release(msg)
}

/*
*
释放资源
*/
func release(msg *QuitMsg) {
	cli := GetClientById(msg.ClientId)
	releaseByConn(cli.Conn)
}

func releaseByConn(conn *net.TCPConn) {
	client := GetClientByConn(conn)
	if client != nil {
		DeleteClientById(client.ClientId)
	}
	DeleteClientByConn(conn)
}
